package studio.baka.satoripixeldungeon.items.artifacts;

import studio.baka.satoripixeldungeon.Assets;
import studio.baka.satoripixeldungeon.Dungeon;
import studio.baka.satoripixeldungeon.actors.Actor;
import studio.baka.satoripixeldungeon.actors.Char;
import studio.baka.satoripixeldungeon.actors.buffs.Buff;
import studio.baka.satoripixeldungeon.actors.buffs.Invisibility;
import studio.baka.satoripixeldungeon.actors.buffs.LockedFloor;
import studio.baka.satoripixeldungeon.actors.hero.Hero;
import studio.baka.satoripixeldungeon.actors.mobs.Mob;
import studio.baka.satoripixeldungeon.effects.MagicMissile;
import studio.baka.satoripixeldungeon.items.Item;
import studio.baka.satoripixeldungeon.items.scrolls.ScrollOfTeleportation;
import studio.baka.satoripixeldungeon.mechanics.Ballistica;
import studio.baka.satoripixeldungeon.messages.Messages;
import studio.baka.satoripixeldungeon.plants.Swiftthistle;
import studio.baka.satoripixeldungeon.scenes.CellSelector;
import studio.baka.satoripixeldungeon.scenes.GameScene;
import studio.baka.satoripixeldungeon.scenes.InterlevelScene;
import studio.baka.satoripixeldungeon.sprites.ItemSprite.Glowing;
import studio.baka.satoripixeldungeon.sprites.ItemSpriteSheet;
import studio.baka.satoripixeldungeon.ui.QuickSlotButton;
import studio.baka.satoripixeldungeon.utils.GLog;
import com.watabou.noosa.Game;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle;
import com.watabou.utils.PathFinder;

import java.util.ArrayList;
import java.util.Objects;

public class LloydsBeacon extends Artifact {

    public static final float TIME_TO_USE = 1;

    public static final String AC_ZAP = "ZAP";
    public static final String AC_SET = "SET";
    public static final String AC_RETURN = "RETURN";

    public int returnDepth = -1;
    public int returnPos;

    {
        image = ItemSpriteSheet.ARTIFACT_BEACON;

        levelCap = 3;

        charge = 0;
        chargeCap = 3 + level();

        defaultAction = AC_ZAP;
        usesTargeting = true;
    }

    private static final String DEPTH = "depth";
    private static final String POS = "pos";

    @Override
    public void storeInBundle(Bundle bundle) {
        super.storeInBundle(bundle);
        bundle.put(DEPTH, returnDepth);
        if (returnDepth != -1) {
            bundle.put(POS, returnPos);
        }
    }

    @Override
    public void restoreFromBundle(Bundle bundle) {
        super.restoreFromBundle(bundle);
        returnDepth = bundle.getInt(DEPTH);
        returnPos = bundle.getInt(POS);
    }

    @Override
    public ArrayList<String> actions(Hero hero) {
        ArrayList<String> actions = super.actions(hero);
        actions.add(AC_ZAP);
        actions.add(AC_SET);
        if (returnDepth != -1) {
            actions.add(AC_RETURN);
        }
        return actions;
    }

    @Override
    public void execute(Hero hero, String action) {

        super.execute(hero, action);

        if (Objects.equals(action, AC_SET) || Objects.equals(action, AC_RETURN)) {

            if (Dungeon.bossLevel()) {
                hero.spend(LloydsBeacon.TIME_TO_USE);
                GLog.w(Messages.get(this, "preventing"));
                return;
            }

            for (int i = 0; i < PathFinder.NEIGHBOURS8.length; i++) {
                Char ch = Actor.findChar(hero.pos + PathFinder.NEIGHBOURS8[i]);
                if (ch != null && ch.alignment == Char.Alignment.ENEMY) {
                    GLog.w(Messages.get(this, "creatures"));
                    return;
                }
            }
        }

        if (Objects.equals(action, AC_ZAP)) {

            curUser = hero;
            int chargesToUse = Dungeon.depth > 20 ? 2 : 1;

            if (!isEquipped(hero)) {
                GLog.i(Messages.get(Artifact.class, "need_to_equip"));
                QuickSlotButton.cancel();

            } else if (charge < chargesToUse) {
                GLog.i(Messages.get(this, "no_charge"));
                QuickSlotButton.cancel();

            } else {
                GameScene.selectCell(zapper);
            }

        } else if (Objects.equals(action, AC_SET)) {

            returnDepth = Dungeon.depth;
            returnPos = hero.pos;

            hero.spend(LloydsBeacon.TIME_TO_USE);
            hero.busy();

            hero.sprite.operate(hero.pos);
            Sample.INSTANCE.play(Assets.SND_BEACON);

            GLog.i(Messages.get(this, "return"));

        } else if (Objects.equals(action, AC_RETURN)) {

            if (returnDepth == Dungeon.depth) {
                ScrollOfTeleportation.appear(hero, returnPos);
                for (Mob m : Dungeon.level.mobs) {
                    if (m.pos == hero.pos) {
                        //displace mob
                        for (int i : PathFinder.NEIGHBOURS8) {
                            if (Actor.findChar(m.pos + i) == null && Dungeon.level.passable[m.pos + i]) {
                                m.pos += i;
                                m.sprite.point(m.sprite.worldToCamera(m.pos));
                                break;
                            }
                        }
                    }
                }
                Dungeon.level.occupyCell(hero);
                Dungeon.observe();
                GameScene.updateFog();
            } else {

                Buff buff = Dungeon.hero.buff(TimekeepersHourglass.timeFreeze.class);
                if (buff != null) buff.detach();
                buff = Dungeon.hero.buff(Swiftthistle.TimeBubble.class);
                if (buff != null) buff.detach();

                InterlevelScene.mode = InterlevelScene.Mode.RETURN;
                InterlevelScene.returnDepth = returnDepth;
                InterlevelScene.returnPos = returnPos;
                Game.switchScene(InterlevelScene.class);
            }


        }
    }

    protected CellSelector.Listener zapper = new CellSelector.Listener() {

        @Override
        public void onSelect(Integer target) {

            if (target == null) return;

            Invisibility.dispel();
            charge -= Dungeon.depth > 20 ? 2 : 1;
            updateQuickslot();

            if (Actor.findChar(target) == curUser) {
                ScrollOfTeleportation.teleportHero(curUser);
                curUser.spendAndNext(1f);
            } else {
                final Ballistica bolt = new Ballistica(curUser.pos, target, Ballistica.MAGIC_BOLT);
                final Char ch = Actor.findChar(bolt.collisionPos);

                if (ch == curUser) {
                    ScrollOfTeleportation.teleportHero(curUser);
                    curUser.spendAndNext(1f);
                } else {
                    Sample.INSTANCE.play(Assets.SND_ZAP);
                    curUser.sprite.zap(bolt.collisionPos);
                    curUser.busy();

                    MagicMissile.boltFromChar(curUser.sprite.parent,
                            MagicMissile.BEACON,
                            curUser.sprite,
                            bolt.collisionPos,
                            () -> {
                                if (ch != null) {

                                    int count = 10;
                                    int pos;
                                    do {
                                        pos = Dungeon.level.randomRespawnCell();
                                        if (count-- <= 0) {
                                            break;
                                        }
                                    } while (pos == -1);

                                    if (pos == -1 || Dungeon.bossLevel()) {

                                        GLog.w(Messages.get(ScrollOfTeleportation.class, "no_tele"));

                                    } else if (ch.properties().contains(Char.Property.IMMOVABLE)) {

                                        GLog.w(Messages.get(LloydsBeacon.class, "tele_fail"));

                                    } else {

                                        ch.pos = pos;
                                        if (ch instanceof Mob && ((Mob) ch).state == ((Mob) ch).HUNTING) {
                                            ((Mob) ch).state = ((Mob) ch).WANDERING;
                                        }
                                        ch.sprite.place(ch.pos);
                                        ch.sprite.visible = Dungeon.level.heroFOV[pos];

                                    }
                                }
                                curUser.spendAndNext(1f);
                            });

                }


            }

        }

        @Override
        public String prompt() {
            return Messages.get(LloydsBeacon.class, "prompt");
        }
    };

    @Override
    protected ArtifactBuff passiveBuff() {
        return new beaconRecharge();
    }

    @Override
    public void charge(Hero target) {
        if (charge < chargeCap) {
            partialCharge += 0.25f;
            if (partialCharge >= 1) {
                partialCharge--;
                charge++;
                updateQuickslot();
            }
        }
    }

    @Override
    public Item upgrade() {
        if (level() == levelCap) return this;
        chargeCap++;
        GLog.p(Messages.get(this, "levelup"));
        return super.upgrade();
    }

    @Override
    public String desc() {
        String desc = super.desc();
        if (returnDepth != -1) {
            desc += "\n\n" + Messages.get(this, "desc_set", returnDepth);
        }
        return desc;
    }

    private static final Glowing WHITE = new Glowing(0xFFFFFF);

    @Override
    public Glowing glowing() {
        return returnDepth != -1 ? WHITE : null;
    }

    public class beaconRecharge extends ArtifactBuff {
        @Override
        public boolean act() {
            LockedFloor lock = target.buff(LockedFloor.class);
            if (charge < chargeCap && !cursed && (lock == null || lock.regenOn())) {
                partialCharge += 1 / (100f - (chargeCap - charge) * 10f);

                if (partialCharge >= 1) {
                    partialCharge--;
                    charge++;

                    if (charge == chargeCap) {
                        partialCharge = 0;
                    }
                }
            }

            updateQuickslot();
            spend(TICK);
            return true;
        }
    }
}
